---
type: tutorial
title: Build a tag index page
description: |-
  Tutorial: Build your first Astro blog —
  Use everything you've learned so far to build a Tag Index page
i18nReady: true
---
import Box from '~/components/tutorial/Box.astro';
import Checklist from '~/components/Checklist.astro';
import MultipleChoice from '~/components/tutorial/MultipleChoice.astro';
import Option from '~/components/tutorial/Option.astro';
import PreCheck from '~/components/tutorial/PreCheck.astro';
import { Steps } from '@astrojs/starlight/components';

Now that you have individual pages for every tag, it's time to make links to them.

<PreCheck>
  - Add a new page using the `/pages/folder/index.astro` routing pattern
  - Display a list of all your unique tags, linking to each tag page
  - Update your site with navigation links to this new Tags page
</PreCheck>

## Use the `/pages/folder/index.astro` routing pattern

To add a Tag Index page to your website, you could create a new file at `src/pages/tags.astro`. 

But, since you already have the directory `/tags/`, you can take advantage of another routing pattern in Astro, and keep all your files related to tags together.

<Box icon="puzzle-piece">

## Try it yourself - Make a Tag Index page

<Steps>
1. Create a new file `index.astro` in the directory `src/pages/tags/`.

2. Navigate to `http://localhost:4321/tags` and verify that your site now contains a page at this URL. It will be empty, but it will exist.

3. Create a minimal page at `src/pages/tags/index.astro` that uses your layout. You have done this before!

    <details>
      <summary>Expand to see the steps</summary>
      <Steps>
      1. Create a new page component in `src/pages/tags/`.

          <details>
          <summary>Show the filename</summary>
          ```
          index.astro
          ```
          </details>

      2. Import and use your `<BaseLayout>`.

          <details>
          <summary>Show the code</summary>
          ```astro title=" src/pages/tags/index.astro"
          ---
          import BaseLayout from '../../layouts/BaseLayout.astro';
          ---
          <BaseLayout></BaseLayout>
          ```
          </details>

      3. Define a page title, and pass it to your layout as a component attribute.

          <details>
          <summary>Show the code</summary>
          ```astro title="src/pages/tags/index.astro" ins={3} "pageTitle"
          ---
          import BaseLayout from '../../layouts/BaseLayout.astro';
          const pageTitle = "Tag Index";
          ---
          <BaseLayout pageTitle={pageTitle}></BaseLayout>
          ```
          </details>
      </Steps>
    </details>

4. Check your browser preview again and you should have a formatted page, ready to add content to!
</Steps>
</Box>

## Create an array of tags

You have previously displayed items in a list from an array using `map()`. What would it look like to define an array of all your tags, then display them in a list on this page?

<details>
    <summary>See the code</summary>
    
    ```astro title="src/pages/tags/index.astro"
    ---
    import BaseLayout from '../../layouts/BaseLayout.astro';    
    const tags = ['astro', 'blogging', 'learning in public', 'successes', 'setbacks', 'community']
    const pageTitle = "Tag Index";
    ---
    <BaseLayout pageTitle={pageTitle}>
      <ul>
        {tags.map((tag) => <li>{tag}</li>)}
      </ul>
    </BaseLayout>
    ```
</details>

You could do this, but then you would need to come back to this file and update your array every time you use a new tag in a future blog post.

Fortunately, you already know a way to grab the data from all your Markdown files in one line of code, then return a list of all your tags.

<Steps>
1. In `src/pages/tags/index.astro`, add the line of code to the frontmatter script that will give your page access to the data from every `.md` blog post file.

    <details>
    <summary>See the code</summary>
    ```astro title = "src/pages/tags/index.astro" ins={3}
    ---
    import BaseLayout from '../../layouts/BaseLayout.astro';
    const allPosts = await Astro.glob('../posts/*.md');
    const pageTitle = "Tag Index";
    ---
    ```
    </details>

2. Next, add the following line of JavaScript to your page component. This is the same one you used in `src/pages/tags/[tag].astro` to return a list of unique tags.

    ```astro title = "src/pages/tags/index.astro" ins={4}
    ---
    import BaseLayout from '../../layouts/BaseLayout.astro';
    const allPosts = await Astro.glob('../posts/*.md');
    const tags = [...new Set(allPosts.map((post) => post.frontmatter.tags).flat())];
    const pageTitle = "Tag Index";
    ---
    
    ```
</Steps>

## Create your list of tags

Instead of creating items in an unordered list this time, create one `<p>` for each item, inside a `<div>`. The pattern should look familiar!

<Steps>
1. Add the following code to your component template:

    ```astro title="src/pages/tags/index.astro" ins={2}
    <BaseLayout pageTitle={pageTitle}>
      <div>{tags.map((tag) => <p>{tag}</p>)}</div>
    </BaseLayout>
    ```
    In your browser preview, verify that you can see your tags listed.

2. To make each tag link to its own page, add the following `<a>` link to each tag name:

    ```astro title="src/pages/tags/index.astro" '/tags/${tag}'
    <BaseLayout pageTitle={pageTitle}>
      <div>
        {tags.map((tag) => (
          <p><a href={`/tags/${tag}`}>{tag}</a></p>
        ))}
      </div>
    </BaseLayout>
    ```
</Steps>

## Add styles to your tag list

<Steps>
1. Add the following CSS classes to style both your `<div>` and each `<p>` that will be generated. Note: Astro uses HTML syntax for adding class names!

    ```astro title="src/pages/tags/index.astro" 'class="tags"' 'class="tag"'
    <BaseLayout pageTitle={pageTitle}>
      <div class="tags">
        {tags.map((tag) => (
          <p class="tag"><a href={`/tags/${tag}`}>{tag}</a></p>
        ))}
      </div>
    </BaseLayout>
    ```

2. Define these new CSS classes by adding the following `<style>` tag to this page:

    ```astro title="src/pages/tags/index.astro"
    <style>
      a {
        color: #00539F;
      }

      .tags {
        display: flex; 
        flex-wrap: wrap; 
      }

      .tag {
        margin: 0.25em;
        border: dotted 1px #a1a1a1;
        border-radius: .5em;
        padding: .5em 1em;
        font-size: 1.15em;
        background-color: #F8FCFD;
      }
    </style>
    ```

3. Check your browser preview at `http://localhost:4321/tags` to verify that you have some new styles and that each of the tags on the page has a working link to its own individual tag page.
</Steps>

### Code Check-In

Here is what your new page should look like:

```astro title="src/pages/tags/index.astro"
--- 
import BaseLayout from '../../layouts/BaseLayout.astro';
const allPosts = await Astro.glob('../posts/*.md');
const tags = [...new Set(allPosts.map((post) => post.frontmatter.tags).flat())];
const pageTitle = "Tag Index";
---
<BaseLayout pageTitle={pageTitle}>
  <div class="tags">
    {tags.map((tag) => (
      <p class="tag"><a href={`/tags/${tag}`}>{tag}</a></p>
    ))}
  </div>
</BaseLayout>
<style>
  a {
    color: #00539F;
  }

  .tags {
    display: flex; 
    flex-wrap: wrap; 
  }

  .tag {
    margin: 0.25em;
    border: dotted 1px #a1a1a1;
    border-radius: .5em;
    padding: .5em 1em;
    font-size: 1.15em;
    background-color: #F8FCFD;
  }
</style>
```

## Add this page to your navigation

Right now, you can navigate to `http://localhost:4321/tags` and see this page. From this page, you can click on links to your individual tag pages.

But, you still need to make these pages discoverable from other pages on your website.

<Steps>
1. In your `Navigation.astro` component, include a link to this new tag index page.

    <details>
    <summary>Show me the code</summary>
    ```astro title="src/components/Navigation.astro" ins={4}
    <a href="/">Home</a>
    <a href="/about/">About</a>
    <a href="/blog/">Blog</a>
    <a href="/tags/">Tags</a>
    ```
    </details>
</Steps>

<Box icon="puzzle-piece">

## Challenge: Include tags in your blog post layout

You have now written all the code you need to also display a list of tags on each blog post, and link them to their tag pages. You have existing work that you can reuse!

Follow the steps below, then check your work by comparing it to the [final code sample](#code-check-in-markdownpostlayout).
<Steps>

1. Copy the `<div class="tags">...</div>` and `<style>...</style>` from `src/pages/tags/index.astro` and reuse it inside `MarkdownPostLayout.astro`: 

    ```astro title="src/layouts/MarkdownPostLayout.astro" ins={13-17, 21-40}
    ---
    import BaseLayout from './BaseLayout.astro';
    const { frontmatter } = Astro.props;
    --- 
    <BaseLayout pageTitle={frontmatter.title}>
      <p><em>{frontmatter.description}</em></p>
      <p>{frontmatter.pubDate.toString().slice(0,10)}</p>

      <p>Written by: {frontmatter.author}</p>

      <img src={frontmatter.image.url} width="300" alt={frontmatter.image.alt} /> 

      <div class="tags">
        {tags.map((tag) => (
          <p class="tag"><a href={`/tags/${tag}`}>{tag}</a></p>
        ))}
      </div>

      <slot />
    </BaseLayout>
    <style>
      a {
        color: #00539F;
      }

      .tags {
        display: flex; 
        flex-wrap: wrap; 
      }

      .tag {
        margin: 0.25em;
        border: dotted 1px #a1a1a1;
        border-radius: .5em;
        padding: .5em 1em;
        font-size: 1.15em;
        background-color: #F8FCFD;
      }
    </style>
    ```

</Steps>

Before this code will work, you need to make **one small edit** to the code you pasted into `MarkdownPostLayout.astro`. Can you figure out what it is?

<details>
<summary>Give me a hint</summary>

How are the other props (e.g. title, author, etc.) written in your layout template? How does your layout receive props from an individual blog post?
</details>

<details>
<summary>Give me another hint!</summary>

In order to use props (values passed) from a `.md` blog post in your layout, like tags, you need to prefix the value with a certain word.

<details>
<summary>Show me the code!</summary>

```astro title="src/layouts/MarkdownPostLayout.astro" "frontmatter"
    <div class="tags">
      {frontmatter.tags.map((tag) => (
        <p class="tag"><a href={`/tags/${tag}`}>{tag}</a></p>
      ))}
    </div>
```
</details>
</details>
</Box>

### Code Check-in: MarkdownPostLayout

To check your work, or if you just want complete, correct code to copy into `MarkdownPostLayout.astro`, here is what your Astro component should look like:

```astro title="src/layouts/MarkdownPostLayout.astro"
---
import BaseLayout from './BaseLayout.astro';
const { frontmatter } = Astro.props;
--- 
<BaseLayout pageTitle={frontmatter.title}>
  <p><em>{frontmatter.description}</em></p>
  <p>{frontmatter.pubDate.toString().slice(0,10)}</p>

  <p>Written by: {frontmatter.author}</p>

  <img src={frontmatter.image.url} width="300" alt={frontmatter.image.alt} /> 

  <div class="tags">
    {frontmatter.tags.map((tag) => (
      <p class="tag"><a href={`/tags/${tag}`}>{tag}</a></p>
    ))}
  </div>

  <slot />
</BaseLayout>
<style>
  a {
    color: #00539F;
  }

  .tags {
    display: flex; 
    flex-wrap: wrap; 
  }

  .tag {
    margin: 0.25em;
    border: dotted 1px #a1a1a1;
    border-radius: .5em;
    padding: .5em 1em;
    font-size: 1.15em;
    background-color: #F8FCFD;
  }
</style>
```



<Box icon="question-mark">

### Test your knowledge

Match each file path with a second file path that will create a page at the same route.

1. `src/pages/categories.astro`

    <MultipleChoice>
      <Option>`src/pages/posts/post.astro`</Option>
      <Option>`src/pages/posts/index.astro`</Option>
      <Option>`src/components/shoes/Shoe.astro`</Option>
      <Option isCorrect>`src/pages/categories/index.astro`</Option>
    </MultipleChoice>

2. `src/pages/posts.astro`

    <MultipleChoice>
      <Option>`src/pages/products/shoes.astro`</Option>
      <Option>`src/pages/posts/post.astro`</Option>
      <Option isCorrect>`src/pages/posts/index.astro`</Option>
      <Option>`src/pages/categories/index.astro`</Option>
    </MultipleChoice>

3. `src/pages/products/shoes/index.astro`

    <MultipleChoice>
      <Option isCorrect>`src/pages/products/shoes.astro`</Option>
      <Option>`src/pages/posts/post.astro`</Option>
      <Option>`src/pages/posts/index.astro`</Option>
      <Option>`src/components/shoes/Shoe.astro`</Option>
    </MultipleChoice>

</Box>

<Box icon="check-list">

## Checklist

<Checklist>
- [ ] I can use Astro's `/pages/folder/index.astro` routing feature.
</Checklist>
</Box>

### Resources

- [Static Routing in Astro](/en/guides/routing/#static-routes)
